import crypto from "node:crypto";

/** 返回 nodejs crypto 模块生成的 v4 版本 uuid（长度为36的字符串，其中包含了32个16进制字符和4个连接字符）
 * @v1 基于【时间和节点】生成的UUid，基于当前时间戳和节点的MAC地址生成，保证UUid在同一节点上具有唯一性。其中包括时间戳、时钟序列、节点ID等信息。
 * @v2 基于【DCE 安全规范】生成的UUid，不常用，使用POSIX UID/GID的48位空间和当前时间生成UUid。与版本1相似，但包括POSIX UID/GID的信息。
 * @v3 基于【MD5】生成的的UUid，使用MD5散列算法，将命名空间和名称进行散列，生成UUid。确保相同命名空间和名称的输入始终生成相同的UUid。
 * @v4 基于【随机122bits】生成的UUid，完全基于随机数生成，包括随机产生的48位数。这是最常见的用于生成UUid的版本，提供了极佳的唯一性
 * @v5 基于【SHA-1 + 指定字符】生成的UUid，完全基于随机数生成，包括随机产生的48位数。这是最常见的用于生成UUid的版本，提供了极佳的唯一性
 */
export function getUUid32() {
  return crypto.randomUUID(); // 12345678-1234-1234-1234-123456781234
}

/** 获取任意长度的随机字符串（有概率重复） */
export function getRandomString(length = 16): string {
  return crypto
    .randomBytes(Math.ceil(length / 2))
    .toString("hex") // 将随机字节转换为十六进制字符串（0123456789abcdef）
    .slice(0, length); // 截取指定长度的字符串
}

/** 交易号 */
export function getTradeId(): string {
  return `${getUUid32()}:${getRandomString(12)}`;
}

/** 将字符串等分成数组
 * @param {string} str 要分割的字符串
 * @param {number} size 每个部分的长度
 * @returns {string[]} 返回包含等分后的部分的数组 [abc, def, ghi]
 */
export function splitString(mess: string, size: number): string[] {
  const regex = new RegExp(`.{1,${size}}`, "g");
  return mess.match(regex) || [];
}

/** 获取任意长度的随机字符串（有概率重复）
 * @tip 正常 ASCII/UTF-8 字符需要占用 1byte 空间
 * @tip 16进制的字符仅占用 4bit 空间
 * @tip 所以，如果要一个长度是64的十六进制字符，就需要 32bytes 空间
 * @tip 再进一步，如果从任意地方剥离 32bytes 内容，就能渠道长度是64的十六进制字符
 */
export function getRandomBytes(length = 16) {
  const buffer = crypto.randomBytes(length);
  return {
    buffer: buffer,
    hex: buffer.toString("hex"),
    acsii: buffer.toString("ascii"),
  };
}

/** 加密文本（AES-256-CBC）
 * @hex64 唯一密钥
 * @tip AES-256-CBC 是一种对称加密算法，全称为高级加密标准（Advanced Encryption Standard）
 * @tip 使用 256 位密钥长度和 CBC（Cipher Block Chaining）模式。
 * @tip 它是由美国国家标准技术研究所（NIST）通过公开征求意见的过程选择出来的。
 */
export function encryptTextByAES256(text: string, hex64: string) {
  const salt = getRandomBytes(16);
  const secretBytes32 = Buffer.from(hex64, "hex") as Uint8Array;

  const cipher = crypto.createCipheriv("aes-256-cbc", secretBytes32, salt.buffer as Uint8Array);
  const encrypted = Buffer.concat([
    //
    cipher.update(text, "ascii") as Uint8Array,
    cipher.final() as Uint8Array,
  ]).toString("hex");

  return `${salt.hex}${encrypted}`;
}

/** 解密文本（AES-256-CBC）
 * @hex64 唯一密钥
 */
export function decryptTextByAES256(encryptedText: string, hex64: string) {
  const saltBuffer = Buffer.from(encryptedText.slice(0, 32), "hex");
  const secretBytes32 = Buffer.from(hex64, "hex") as Uint8Array;
  const encryptedBuffer = Buffer.from(encryptedText.slice(32), "hex") as Uint8Array;

  const decipher = crypto.createDecipheriv("aes-256-cbc", secretBytes32, saltBuffer as Uint8Array);
  const decryptedBuffer = Buffer.concat([
    //
    decipher.update(encryptedBuffer) as Uint8Array,
    decipher.final() as Uint8Array,
  ]);
  return decryptedBuffer.toString("ascii");
}
